/*     */ package com.zimbra.cs.fb;
/*     */ 
/*     */ import com.zimbra.common.calendar.ICalTimeZone;
/*     */ import com.zimbra.common.calendar.ParsedDateTime;
/*     */ import com.zimbra.common.calendar.ParsedDuration;
/*     */ import com.zimbra.common.calendar.TimeZoneMap;
/*     */ import com.zimbra.common.calendar.ZWeekDay;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.cs.account.AccessManager;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.accesscontrol.Rights.User;
/*     */ import com.zimbra.cs.mailbox.CalendarItem.Instance;
/*     */ import com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence;
/*     */ import com.zimbra.cs.mailbox.calendar.Recurrence.SimpleRepeatingRule;
/*     */ import com.zimbra.cs.mailbox.calendar.Util;
/*     */ import com.zimbra.cs.mailbox.calendar.ZRecur;
/*     */ import java.text.ParseException;
/*     */ import java.util.Calendar;
/*     */ import java.util.GregorianCalendar;
/*     */ import java.util.HashMap;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class WorkingHours
/*     */ {
/*     */   public static FreeBusy getWorkingHours(Account authAcct, boolean asAdmin, Account account, String name, long start, long end)
/*     */     throws ServiceException
/*     */   {
/*  51 */     AccessManager accessMgr = AccessManager.getInstance();
/*  52 */     boolean accountAceAllowed = accessMgr.canDo(authAcct, account, Rights.User.R_viewFreeBusy, asAdmin);
/*  53 */     if (!accountAceAllowed) {
/*  54 */       return FreeBusy.nodataFreeBusy(account.getName(), start, end);
/*     */     }
/*     */     
/*  57 */     String workingHoursPref = account.getPrefCalendarWorkingHours();
/*  58 */     HoursByDay workingHoursByDay = new HoursByDay(workingHoursPref);
/*     */     
/*     */ 
/*  61 */     FreeBusy.IntervalList intervals = new FreeBusy.IntervalList(start, end);
/*  62 */     ICalTimeZone tz = Util.getAccountTimeZone(account);
/*  63 */     TimeZoneMap tzmap = new TimeZoneMap(tz);
/*  64 */     StartSpec startSpec = new StartSpec(start, tz);
/*  65 */     for (int day = 1; day <= 7; day++) {
/*  66 */       TimeRange timeRange = workingHoursByDay.getHoursForDay(day);
/*  67 */       if (timeRange.enabled) {
/*  68 */         Recurrence.IRecurrence rrule = getRecurrenceForDay(day, startSpec, timeRange, tz, tzmap);
/*  69 */         List<CalendarItem.Instance> instances = rrule.expandInstances(0, start, end);
/*  70 */         for (CalendarItem.Instance inst : instances) {
/*  71 */           FreeBusy.Interval ival = new FreeBusy.Interval(inst.getStart(), inst.getEnd(), "O");
/*  72 */           intervals.addInterval(ival);
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*  78 */     for (Iterator<FreeBusy.Interval> iter = intervals.iterator(); iter.hasNext();) {
/*  79 */       FreeBusy.Interval interval = (FreeBusy.Interval)iter.next();
/*  80 */       String status = interval.getStatus();
/*  81 */       interval.setStatus(invertStatus(status));
/*     */     }
/*  83 */     return new FreeBusy(name, intervals, start, end);
/*     */   }
/*     */   
/*     */   public static void validateWorkingHoursPref(String pref) throws ServiceException {
/*  87 */     new HoursByDay(pref);
/*     */   }
/*     */   
/*     */   private static String invertStatus(String status) {
/*  91 */     if ("F".equals(status)) {
/*  92 */       return "O";
/*     */     }
/*  94 */     return "F";
/*     */   }
/*     */   
/*     */   private static Recurrence.IRecurrence getRecurrenceForDay(int dayOfWeek, StartSpec startSpec, TimeRange timeRange, ICalTimeZone tz, TimeZoneMap tzmap)
/*     */     throws ServiceException
/*     */   {
/* 100 */     String dateStr = startSpec.getDateString(dayOfWeek);
/*     */     String dtStartStr;
/* 102 */     String dtStartStr; if (tz.sameAsUTC()) {
/* 103 */       dtStartStr = String.format("%sT%02d%02d00Z", new Object[] { dateStr, Integer.valueOf(timeRange.start.hour), Integer.valueOf(timeRange.start.minute) });
/*     */     } else
/* 105 */       dtStartStr = String.format("TZID=\"%s\":%sT%02d%02d00", new Object[] { tz.getID(), dateStr, Integer.valueOf(timeRange.start.hour), Integer.valueOf(timeRange.start.minute) });
/*     */     ParsedDateTime dtStart;
/*     */     try {
/* 108 */       dtStart = ParsedDateTime.parse(dtStartStr, tzmap);
/*     */     } catch (ParseException e) {
/* 110 */       throw ServiceException.INVALID_REQUEST("Bad date/time value \"" + dtStartStr + "\"", e);
/*     */     }
/*     */     
/* 113 */     ParsedDuration dur = timeRange.getDuration();
/*     */     
/* 115 */     String dayName = DayOfWeekName.lookup(dayOfWeek);
/* 116 */     String ruleStr = String.format("FREQ=WEEKLY;INTERVAL=1;BYDAY=%s", new Object[] { dayName });
/* 117 */     return new Recurrence.SimpleRepeatingRule(dtStart, dur, new ZRecur(ruleStr, tzmap), null);
/*     */   }
/*     */   
/*     */   private static class DayOfWeekName {
/* 121 */     private static ZWeekDay[] sNames = { ZWeekDay.SU, ZWeekDay.MO, ZWeekDay.TU, ZWeekDay.WE, ZWeekDay.TH, ZWeekDay.FR, ZWeekDay.SA };
/*     */     
/*     */     public static String lookup(int dayOfWeek) throws ServiceException
/*     */     {
/* 125 */       if ((dayOfWeek >= 1) && (dayOfWeek <= sNames.length))
/* 126 */         return sNames[(dayOfWeek - 1)].toString();
/* 127 */       throw ServiceException.INVALID_REQUEST("Invalid day of week " + dayOfWeek, null);
/*     */     }
/*     */   }
/*     */   
/*     */   private static class HourMinute {
/*     */     public int hour;
/*     */     public int minute;
/*     */     
/* 135 */     public HourMinute(int hh, int mm) { this.hour = hh;
/* 136 */       this.minute = mm;
/*     */     }
/*     */     
/*     */     public boolean laterThan(HourMinute other) {
/* 140 */       return (this.hour > other.hour) || ((this.hour == other.hour) && (this.minute > other.minute));
/*     */     }
/*     */     
/*     */ 
/*     */     public HourMinute(String hhmmStr, boolean isEndTime, String dayStr)
/*     */       throws ServiceException
/*     */     {
/* 147 */       boolean good = false;
/* 148 */       if (hhmmStr.length() == 4) {
/*     */         try {
/* 150 */           int hh = Integer.parseInt(hhmmStr.substring(0, 2));
/* 151 */           int mm = Integer.parseInt(hhmmStr.substring(2));
/* 152 */           if (((hh >= 0) && (hh <= 23) && (mm >= 0) && (mm <= 59)) || ((isEndTime) && (hh == 24) && (mm == 0))) {
/* 153 */             this.hour = hh;
/* 154 */             this.minute = mm;
/* 155 */             good = true;
/*     */           }
/*     */         } catch (NumberFormatException e) {}
/*     */       }
/* 159 */       if (!good) {
/* 160 */         throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + dayStr + "\" has invalid " + (isEndTime ? "end" : "start") + " time \"" + hhmmStr + "\"", null);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static class TimeRange {
/*     */     public WorkingHours.HourMinute start;
/*     */     public WorkingHours.HourMinute end;
/*     */     public boolean enabled;
/*     */     
/*     */     public TimeRange(WorkingHours.HourMinute start, WorkingHours.HourMinute end, boolean enabled) {
/* 171 */       this.start = start;
/* 172 */       this.end = end;
/* 173 */       this.enabled = enabled;
/*     */     }
/*     */     
/* 176 */     public ParsedDuration getDuration() { int diff = this.end.hour * 60 + this.end.minute - (this.start.hour * 60 + this.start.minute);
/* 177 */       int hours = diff / 60;
/* 178 */       int minutes = diff % 60;
/* 179 */       return ParsedDuration.parse(false, 0, 0, hours, minutes, 0);
/*     */     }
/*     */   }
/*     */   
/*     */   private static class HoursByDay
/*     */   {
/* 185 */     private Map<Integer, WorkingHours.TimeRange> mHours = new HashMap(7);
/*     */     
/*     */     public HoursByDay() {
/* 188 */       WorkingHours.HourMinute hhmm0000 = new WorkingHours.HourMinute(0, 0);
/* 189 */       WorkingHours.HourMinute hhmm2400 = new WorkingHours.HourMinute(24, 0);
/* 190 */       WorkingHours.TimeRange allHours = new WorkingHours.TimeRange(hhmm0000, hhmm2400, false);
/* 191 */       for (int day = 1; day <= 7; day++) {
/* 192 */         this.mHours.put(Integer.valueOf(day), allHours);
/*     */       }
/*     */     }
/*     */     
/*     */     public WorkingHours.TimeRange getHoursForDay(int dayNum) {
/* 197 */       return (WorkingHours.TimeRange)this.mHours.get(Integer.valueOf(dayNum));
/*     */     }
/*     */     
/*     */     public HoursByDay(String prefStr) throws ServiceException {
/* 201 */       if (!prefStr.matches("[^\\s]+"))
/* 202 */         throw ServiceException.INVALID_REQUEST("Working hours spec should not have whitespaces", null);
/* 203 */       if (prefStr.endsWith(",")) {
/* 204 */         throw ServiceException.INVALID_REQUEST("Working hours spec should not have trailing commas", null);
/*     */       }
/* 206 */       int[] daySpecified = { 0, 0, 0, 0, 0, 0, 0 };
/*     */       
/* 208 */       String[] days = prefStr.split(",");
/* 209 */       if (days.length != 7)
/* 210 */         throw ServiceException.INVALID_REQUEST("Working hours spec must specify all days of a week", null);
/* 211 */       for (int i = 0; i < days.length; i++) {
/* 212 */         if (days[i].endsWith(":")) {
/* 213 */           throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + days[i] + "\" should not have trailing colons", null);
/*     */         }
/* 215 */         String[] parts = days[i].split(":");
/* 216 */         if (parts.length != 4) {
/* 217 */           throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + days[i] + "\" must have 4 colon-separated parts", null);
/*     */         }
/*     */         
/*     */ 
/* 221 */         int dayNum = -1;
/*     */         try {
/* 223 */           dayNum = Integer.parseInt(parts[0]);
/*     */         } catch (NumberFormatException e) {}
/* 225 */         if ((dayNum < 1) || (dayNum > 7)) {
/* 226 */           throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + days[i] + "\" has invalid day number (must be 1 to 7)", null);
/*     */         }
/*     */         
/*     */ 
/* 230 */         if (daySpecified[(dayNum - 1)] != 0) {
/* 231 */           throw ServiceException.INVALID_REQUEST("Working hours spec must not specify the same day more than once; found repeated day " + dayNum, null);
/*     */         }
/* 233 */         daySpecified[(dayNum - 1)] = 1;
/*     */         
/*     */ 
/* 236 */         if (parts[1].length() != 1) {
/* 237 */           throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + days[i] + "\" has invalid on/off flag (must be Y or N)", null);
/*     */         }
/* 239 */         char flag = parts[1].charAt(0);
/* 240 */         if ((flag != 'Y') && (flag != 'N')) {
/* 241 */           throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + days[i] + "\" has invalid on/off flag (must be Y or N)", null);
/*     */         }
/* 243 */         boolean enabled = flag == 'Y';
/*     */         
/*     */ 
/* 246 */         WorkingHours.HourMinute startTime = new WorkingHours.HourMinute(parts[2], false, days[i]);
/*     */         
/*     */ 
/* 249 */         WorkingHours.HourMinute endTime = new WorkingHours.HourMinute(parts[3], true, days[i]);
/*     */         
/*     */ 
/* 252 */         if (startTime.laterThan(endTime)) {
/* 253 */           throw ServiceException.INVALID_REQUEST("Working hours spec day section \"" + days[i] + "\" has end time earlier than start time", null);
/*     */         }
/*     */         
/* 256 */         this.mHours.put(Integer.valueOf(dayNum), new WorkingHours.TimeRange(startTime, endTime, enabled));
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static class StartSpec {
/*     */     private ICalTimeZone mTZ;
/*     */     private long mUtcTime;
/*     */     private Calendar mCal;
/*     */     
/*     */     public StartSpec(long time, ICalTimeZone tz) {
/* 267 */       this.mTZ = tz;
/* 268 */       this.mUtcTime = time;
/* 269 */       this.mCal = new GregorianCalendar(this.mTZ);
/* 270 */       this.mCal.setTimeInMillis(this.mUtcTime);
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */     public String getDateString(int dayOfWeek)
/*     */     {
/* 278 */       int startDayOfWeek = this.mCal.get(7);
/*     */       Calendar cal;
/* 280 */       Calendar cal; if (dayOfWeek == startDayOfWeek) {
/* 281 */         cal = this.mCal;
/*     */       } else {
/* 283 */         int daysDelta = (dayOfWeek + 7 - startDayOfWeek) % 7;
/* 284 */         cal = (Calendar)this.mCal.clone();
/* 285 */         cal.add(6, daysDelta);
/*     */       }
/* 287 */       return String.format("%04d%02d%02d", new Object[] { Integer.valueOf(cal.get(1)), Integer.valueOf(cal.get(2) + 1), Integer.valueOf(cal.get(5)) });
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/fb/WorkingHours.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */